package org.mozhu.mboot.admin.system.resource.service;

import com.google.common.collect.Lists;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.mozhu.mboot.admin.system.account.service.UserService;
import org.mozhu.mboot.admin.system.auth.service.AuthorityService;
import org.mozhu.mboot.admin.system.privilege.entity.Permission;
import org.mozhu.mboot.admin.system.privilege.service.PermissionService;
import org.mozhu.mboot.admin.system.resource.entity.Menu;
import org.mozhu.mboot.admin.system.resource.entity.Resource;
import org.mozhu.mboot.admin.system.resource.repository.ResourceRepository;
import org.mozhu.mboot.core.data.support.BaseTreeableService;
import org.mozhu.mboot.core.security.PermissionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 资源 - 业务层实现类
 *
 * @userAuthor mozhu
 */
@Transactional
@Service(value = "resourceService")
public class ResourceServiceImpl extends BaseTreeableService<Resource, Long> implements ResourceService, MenuService {

    @Autowired
    @Qualifier("authorityService")
    protected AuthorityService authorityService;

    @Autowired
    protected PermissionService permissionService;

    @Autowired
    protected UserService userService;

    public ResourceRepository getBaseRepository() {
        return (ResourceRepository) baseRepository;
    }

    @Override
    public List<Menu> getAvailableMenus(Long userId) {
        List<Resource> resources = this.getBaseRepository().getAllResources();
        Set<String> activePermissions = authorityService.findStringPermissions(userId);
        return getAvailableMenus(resources, activePermissions);
    }

    private List<Menu> getAvailableMenus(List<Resource> resources, Collection<String> activePermissions) {
        Iterator<Resource> iter = resources.iterator();
        while (iter.hasNext()) {
            Resource resource = iter.next();
            if (!resource.getShow() || !hasPermission(resource, activePermissions)) {
                iter.remove();
            }
        }
        return convertToMenus(resources);
    }

    /**
     * 只要Resource中的PermissionCodes有一个不在用户的Permissions中，则返回false
     *
     * @param resource
     * @param authorities
     * @return
     */
    protected boolean hasPermission(Resource resource, Collection<String> authorities) {
        List<String> permissionCodes = resource.getPermissionCodes();
        if (CollectionUtils.isEmpty(permissionCodes)) {
            // 未设置权限code的菜单默认显示
            return true;
        }
        for (String resourcePermission : permissionCodes) {
            if (!PermissionUtils.hasPermission(authorities, resourcePermission)) {
                return false;
            }
        }
        return true;
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<Menu> convertToMenus(List<Resource> resources) {
        if (resources.size() == 0) {
            return Collections.EMPTY_LIST;
        }
        List<Resource> roots = Lists.newArrayList();
        Iterator<Resource> iterator = resources.iterator();
        while (iterator.hasNext()) {
            Resource next = iterator.next();
            if (next.isRoot()) {
                roots.add(next);
                iterator.remove();
            }
        }

        List<Menu> menus = Lists.newArrayList();
        for (Resource resource : roots) {
            Menu rootMenu = convertToMenu(resource);
            recursiveMenu(rootMenu, resources);
            menus.add(rootMenu);
        }
        removeNoLeafMenu(menus);
        return menus;
    }

    /**
     * 移除既不是父节点，又没有url的节点
     *
     * @param menus
     */
    protected void removeNoLeafMenu(List<Menu> menus) {
        if (menus.size() == 0) {
            return;
        }
        for (int i = menus.size() - 1; i >= 0; i--) {
            Menu m = menus.get(i);
            removeNoLeafMenu(m.getChildren());
            if (!m.isHasChildren() && StringUtils.isEmpty(m.getUrl())) {
                menus.remove(i);
            }
        }
    }

    private void recursiveMenu(Menu menu, List<Resource> resources) {
        List<Menu> subMenus = Lists.newArrayList();
        for (int i = resources.size() - 1; i >= 0; i--) {
            Resource resource = resources.get(i);
            if (resource.getParentId().equals(menu.getId())) {
                subMenus.add(convertToMenu(resource));
                resources.remove(i);
            }
        }
        Collections.reverse(subMenus);
        menu.getChildren().addAll(subMenus);

        for (Menu subMenu : menu.getChildren()) {
            recursiveMenu(subMenu, resources);
        }
    }

    private Menu convertToMenu(Resource resource) {
        String icon = resource.getIcon();
        return new Menu(resource.getId(), resource.getCode(), resource.getName(), resource.getEnName(), icon, resource.getUrl());
    }

}
